﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace WarPigs
{
    class Hex
    {
        private float radius;
        private float width;
        private float halfWidth;
        private float height;
        private float rowHeight;


        public Hex(float radius)
        {
            this.radius = radius;
            this.height = 2 * radius;
            this.rowHeight = 1.5f * radius;
            this.halfWidth = (float)Math.Sqrt((radius * radius) - ((radius / 2) * (radius / 2)));
            this.width = 2 * this.halfWidth;
        }

        public Vector2 TileOrigin(Vector2 tileCoordinate)
        {
            return new Vector2(
                (tileCoordinate.X * width) + ((tileCoordinate.Y % 2 == 1) ? halfWidth : 0), //Y % 2 == 1 is asking 'Is Y odd?'
                tileCoordinate.Y * rowHeight);
        }

        public Vector2 TileCenter(Vector2 tileCoordinate)
        {
            return TileOrigin(tileCoordinate) + new Vector2(halfWidth, halfWidth);
        }

        public enum Direction
        {
            NorthEast,
            East,
            SouthEast,
            SouthWest,
            West,
            NorthWest,
            NumberOfDirections,
        }

        public static Direction RotateDirection(Direction direction, int amount)
        {
            //Let's make sure our directions stay within the enumerated values.
            if (direction < Direction.NorthEast || direction > Direction.NorthWest || Math.Abs(amount) > (int)Direction.NorthWest)
                throw new InvalidOperationException("Directions out of range.");

            direction += amount;

            //Now we need to make sure direction stays within the proper range.
            //C# does not allow modulus operations on enums, so we have to convert to and from int.

            int n_dir = (int)direction % (int)Direction.NumberOfDirections;
            if (n_dir < 0) n_dir = (int)Direction.NumberOfDirections + n_dir;
            direction = (Direction)n_dir;

            return direction;
        }

        public static Direction Opposite(Direction direction) { return RotateDirection(direction, 3); }

        public static Vector2 Neighbor(Vector2 tile, Direction direction)
        {
            if (tile.Y % 2 == 0) //Is this row even?
            {
                switch (direction)
                {
                    case Direction.NorthEast: tile.Y += 1; break;
                    case Direction.East: tile.X += 1; break;
                    case Direction.SouthEast: tile.Y -= 1; break;
                    case Direction.SouthWest: tile.Y -= 1; tile.X -= 1; break;
                    case Direction.West: tile.X -= 1; break;
                    case Direction.NorthWest: tile.X -= 1; tile.Y += 1; break;
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }
            else //This is an odd row.
            {
                switch (direction)
                {
                    case Direction.NorthEast: tile.X += 1; tile.Y += 1; break;
                    case Direction.East: tile.X += 1; break;
                    case Direction.SouthEast: tile.X += 1; tile.Y -= 1; break;
                    case Direction.SouthWest: tile.Y -= 1; ; break;
                    case Direction.West: tile.X -= 1; break;
                    case Direction.NorthWest: tile.Y += 1; break;
                    default: throw new InvalidOperationException("Invalid direction");
                }
            }

            return tile;
        }

        public Vector2 TileAt(Vector2 worldCoordinate)
        {
            float rise = height - rowHeight;
            float slope = rise / halfWidth;
            int X = (int)Math.Floor(worldCoordinate.X / width);
            int Y = (int)Math.Floor(worldCoordinate.Y / rowHeight);
            Vector2 offset = new Vector2(worldCoordinate.X - X * width, worldCoordinate.Y - Y * rowHeight);

            if (Y % 2 == 0) //Is this an even row?
            {
                //Section type A
                if (offset.Y < (-slope * offset.X + rise)) //Point is below left line; inside SouthWest neighbor.
                {
                    X -= 1;
                    Y -= 1;
                }
                else if (offset.Y < (slope * offset.X - rise)) //Point is below right line; inside SouthEast neighbor.
                    Y -= 1;
            }
            else
            {
                //Section type B
                if (offset.X >= halfWidth) //Is the point on the right side?
                {
                    if (offset.Y < (-slope * offset.X + rise * 2.0f)) //Point is below bottom line; inside SouthWest neighbor.
                        Y -= 1;
                }
                else //Point is on the left side
                {
                    if (offset.Y < (slope * offset.X)) //Point is below the bottom line; inside SouthWest neighbor.
                        Y -= 1;
                    else //Point is above the bottom line; inside West neighbor.
                        X -= 1;
                }
            }

            return new Vector2(X, Y);
        }

        public static bool IsOddRow(Vector2 tile)
        {
            if (tile.Y % 2 == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}
